#pragma once

#include <Common\File.hpp>
#include <Common\CriticalSection.hpp>
#include <string>
#include <Common\Memory.hpp>

class cSubDataFile : public cSubObject
{
private:
	cFile File;
	cCriticalSection CriticalSection;

	cSubDataFile( cFile &file )
		: File( file )
	{
	}
	~cSubDataFile()
	{
	}

	friend class cDataFile;
};

class cDataFile : public cObject< cSubDataFile >, public cLockObject
{
public:
	bool Create( const char *file_name )
	{
		cFile file;
		if( !file.Create( file_name, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, NULL ) )
			return false;
		cSubDataFile *sub_data_file;
		try
		{
			sub_data_file = new cSubDataFile( file );
		}
		catch( ... )
		{
			return false;
		}
		SwitchToSubObject( sub_data_file );
		return true;
	}
	void Destroy()
	{
		SwitchToSubObject( 0 );
	}
	bool SaveString( const std::string &str, unsigned long &offset )
	{
		if( GetSubObject() == NULL )
			return false;
		if( str.length() != 0 )
		{
			offset = GetSubObject()->File.SetPointer( 0, FILE_END );
			if( offset == INVALID_SET_FILE_POINTER )
				return false;
			DWORD bytes_written;
			if( !GetSubObject()->File.Write( ( LPVOID )str.c_str(), str.length(), &bytes_written, NULL ) )
				return false;
			if( str.length() != bytes_written )
				return false;
		}
		else
		{
			offset = 0;
		}
		return true;
	}
	bool LoadString( unsigned long offset, unsigned long length, std::string &str )
	{
		if( GetSubObject() == NULL )
			return false;
		if( length != 0 )
		{
			if( GetSubObject()->File.SetPointer( ( LONG )offset, FILE_BEGIN ) == INVALID_SET_FILE_POINTER )
				return false;
			cMemory< char > buffer( length );
			DWORD bytes_read;
			if( !GetSubObject()->File.Read( buffer, length, &bytes_read, NULL ) )
				return false;
			if( length != bytes_read )
				return false;
			str = std::string( buffer, length );
		}
		else
		{
			str = "";
		}
		return true;
	}
	bool Flush()
	{
		if( GetSubObject() == NULL )
			return false;
		if( !GetSubObject()->File.Flush() )
			return false;
		return true;
	}
	bool SaveStringAndFlush( const std::string &str, unsigned long &offset )
	{
		if( GetSubObject() == NULL )
			return false;
		cLocker locker( GetSubObject()->CriticalSection );
		if( !SaveString( str, offset ) )
			return false;
		if( !Flush() )
			return false;
		return true;
	}
	bool LoadStringAndFlush( unsigned long offset, unsigned long length, std::string &str )
	{
		if( GetSubObject() == NULL )
			return false;
		cLocker locker( GetSubObject()->CriticalSection );
		if( !LoadString( offset, length, str ) )
			return false;
		if( !Flush() )
			return false;
		return true;
	}
	void Enter() const
	{
		if( GetSubObject() == NULL )
			return;
		GetSubObject()->CriticalSection.Enter();
	}
	void Leave() const
	{
		if( GetSubObject() == NULL )
			return;
		GetSubObject()->CriticalSection.Leave();
	}
};
